package cn.wlinker.driver.modbus.tcp.utils;

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;

/**
 * modbusTcp读取工具类
 *
 * @author gxsjx
 * @version 1.0
 * @date 2022/10/10
 * Copyright © wlinker.cn
 */
public class Modbus4jReader {

    private final ModbusMaster master;

    public Modbus4jReader(String ip, Integer port) {
        this.master = ModbusMasterHelper.getMaster(ip, port);
    }

    /**
     * 读（线圈）开关量数据
     *
     * @param slaveId slaveId
     * @param offset  位置
     * @return 读取值
     */
    public boolean[] readCoilStatus(int slaveId, int offset, int numberOfRegisters)
            throws ModbusTransportException {

        ReadCoilsRequest request = new ReadCoilsRequest(slaveId, offset, numberOfRegisters);
        ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);
        boolean[] booleans = response.getBooleanData();
        return valueRegroup(numberOfRegisters, booleans);
    }

    /**
     * 开关数据 读取外围设备输入的开关量
     */
    public boolean[] readInputStatus(int slaveId, int offset, int numberOfRegisters)
            throws ModbusTransportException {
        ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId, offset, numberOfRegisters);
        ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) master.send(request);
        boolean[] booleans = response.getBooleanData();
        return valueRegroup(numberOfRegisters, booleans);
    }

    /**
     * 读取保持寄存器数据
     *
     * @param slaveId 从机id
     * @param offset 起始地址
     * @param numberOfRegisters 寄存器数量
     * @return 寄存器数据数组
     */
    public short[] readHoldingRegisters(int slaveId, int offset, int numberOfRegisters)
            throws ModbusTransportException {
        ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, offset, numberOfRegisters);
        ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
        return response.getShortData();
    }

    /**
     * 读取保持寄存器数据
     *
     * @param slaveId 从机id
     * @param offset 起始地址
     * @param dataType 数据类型
     * @return 寄存器数据
     */
    public Number readHoldingRegisterByDataType(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> numberBaseLocator = BaseLocator.holdingRegister(slaveId, offset, dataType);
        return master.getValue(numberBaseLocator);
    }

    /**
     * 读取外围设备输入的数据
     *
     * @param slaveId 从机id
     * @param offset 起始地址
     * @param numberOfRegisters 读取数量
     * @return 寄存器数据数组
     */
    public short[] readInputRegisters(int slaveId, int offset, int numberOfRegisters)
            throws ModbusTransportException {
        ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, offset, numberOfRegisters);
        ReadInputRegistersResponse response = (ReadInputRegistersResponse) master.send(request);
        return response.getShortData();
    }

    /**
     * 读取外围设备输入的数据
     *
     * @param slaveId 从机id
     * @param offset 起始地址
     * @param dataType 数据类型
     * @return 寄存器数据
     */
    public Number readInputRegisterByDataType(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        return master.getValue(loc);
    }

    /**
     * 批量读取 可以批量读取不同寄存器中数据
     *
     * @param batch 读取参数
     * @return 读取结果
     */
    public BatchResults<Integer> batchRead(BatchRead<Integer> batch) throws ModbusTransportException, ErrorResponseException {
        batch.setContiguousRequests(true);
        return master.send(batch);
    }

    private boolean[] valueRegroup(int numberOfRegisters, boolean[] values) {
        boolean[] bs = new boolean[numberOfRegisters];
        int temp = 1;
        for (boolean b : values) {
            bs[temp - 1] = b;
            temp++;
            if (temp > numberOfRegisters) {
                break;
            }
        }
        return bs;
    }
}